Skip to content

Prune idle per-host connection semaphores#2277

Merged
hyperxpro merged 1 commit into
AsyncHttpClient:mainfrom
maygemdev:perf/per-host-semaphore-retention
Jul 23, 2026
Merged

Prune idle per-host connection semaphores#2277
hyperxpro merged 1 commit into
AsyncHttpClient:mainfrom
maygemdev:perf/per-host-semaphore-retention

Conversation

@pavel-ptashyts

@pavel-ptashyts pavel-ptashyts commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • track active and waiting users of each per-host connection semaphore
  • remove a host entry after its final permit holder or acquisition attempt leaves
  • preserve the same semaphore while another acquisition is waiting, preventing the per-host limit from being bypassed
  • cover standalone and combined connection limiters with retention and concurrency tests

Motivation

PerHostConnectionSemaphore retained one semaphore and partition key for every host observed by a client. Long-lived clients connecting to many distinct hosts could therefore retain these entries indefinitely.

Testing

  • ./mvnw -pl client -Dtest='org.asynchttpclient.netty.channel.SemaphoreTest#perHostRemovesEntriesAfterLastRelease+combinedRemovesEntriesAfterLastRelease+perHostRetainsEntryDuringWaitingAcquire' test
  • ./mvnw -pl client -Dtest='org.asynchttpclient.netty.channel.SemaphoreTest,org.asynchttpclient.netty.channel.ChannelManagerHttp2DrainPermitTest,org.asynchttpclient.netty.NettyRequestThrottleTimeoutTest' test (346 tests)
  • ./mvnw -B -ntp -pl client -DskipTests -Dmaven.javadoc.skip=true -Dgpg.skip=true verify (includes Revapi)

The required JDK 11 ./mvnw clean verify was not run because JDK 11 is not available in the local environment. CI will exercise the complete supported JDK matrix.

Codex on behalf of Pavel Ptashyts

Per-host connection limits retained one semaphore and partition key for every host observed by the client, even after all permits were returned.

Track holders and waiting acquisitions for each map entry. Remove an entry only after the final reference leaves, so a concurrent acquisition cannot detach from the semaphore used by later callers.

Codex on behalf of Pavel Ptashyts

Co-Authored-By: Codex <[email protected]>
@hyperxpro
hyperxpro merged commit 42cdce4 into AsyncHttpClient:main Jul 23, 2026
25 of 26 checks passed
@pavel-ptashyts
pavel-ptashyts deleted the perf/per-host-semaphore-retention branch July 24, 2026 05:13
hyperxpro added a commit that referenced this pull request Jul 24, 2026
Motivation:

#2277 prevented the per-host connection map from growing indefinitely by
reference counting entries and pruning them when they become idle. This
relies on every release matching a prior reservation. An unmatched or
double release can prematurely remove a live entry, allowing a new
semaphore to be created and silently bypassing the per-host connection
limit. The previous lookup method was also left unused in production
and, if called, could create entries that were never eligible for
pruning. Existing tests covered only a single host.

Modification:

Log unmatched releases so double releases are visible instead of
silently corrupting the reference count. Remove the unused lookup method
and update its remaining test. Add concurrent multi-host stress tests
for both semaphore implementations, verifying that the per-host limit is
enforced under contention and that all entries are pruned once released.

Result:

Unbalanced releases are now detectable, the unused code path has been
removed, and the per-host connection limit is verified under concurrent
multi-host workloads.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants